package com.bjut.vendormgrsys.service.impl;

import com.bjut.vendormgrsys.constant.Constant;
import com.bjut.vendormgrsys.constant.Constant.UserStatus;
import com.bjut.vendormgrsys.constant.Constant.AuditStatus;
import com.bjut.vendormgrsys.model.domain.*;
import com.bjut.vendormgrsys.model.repository.*;
import com.bjut.vendormgrsys.service.UserService;
import com.bjut.vendormgrsys.support.bo.RoleBO;
import com.bjut.vendormgrsys.support.bo.UserBO;
import com.bjut.vendormgrsys.util.DateUtils;
import com.bjut.vendormgrsys.web.dto.*;
import com.bjut.vendormgrsys.web.dto.user.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {

    private final RoleRepository roleRepository;

    private final UserRepository userRepository;

    private final TokenRepository tokenRepository;

    private final UserRoleRespository userRoleRespository;

    private final UserChildRepository userChildRepository;

    private final MailServiceImpl mailService;

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    public UserServiceImpl(RoleRepository roleRepository, UserRepository userRepository, TokenRepository tokenRepository,
                           UserRoleRespository userRoleRespository, UserChildRepository userChildRepository,MailServiceImpl mailService) {
        this.roleRepository = roleRepository;
        this.userRepository = userRepository;
        this.tokenRepository = tokenRepository;
        this.userRoleRespository = userRoleRespository;
        this.userChildRepository = userChildRepository;
        this.mailService = mailService;
    }

    @Override
    @Transactional
    public void insertTokenPO(TokenPO tokenPO) {
        entityManager.persist(tokenPO);
    }

    @Override
    public UserInfo getUserInfoByAccount(String account) {
        // 根据用户账号查询用户信息
        UserPO userPO = userRepository.getUserPOByAccount(account);
        if (userPO == null) {
            return null;
        }
        return getUserInfo(userPO);
    }

    @Override
    public UserInfo getUserInfoById(String id){
        UserPO userPO = userRepository.getUserPOById(id);
        if (userPO == null) {
            return null;
        }
        return getUserInfo(userPO);
    }

    @Override
    @Transactional
    public void saveUserPO(UserInfo userInfo) {
        // for insert
        boolean isPersist = true;
        UserPO userPO = null;
        String id = userInfo.getId();
        List<String> ignorePropList = new ArrayList<>();
        if (StringUtils.hasLength(id)) {
            Optional<UserPO> byId = userRepository.findById(id);
            if (byId.isPresent()) {
                // for update
                userPO = byId.get();
                ignorePropList.add("creator");
                ignorePropList.add("status");
                ignorePropList.add("dateCreated");
                ignorePropList.add("auditsStatus");
                isPersist = false;
            }
        }
        if (userPO == null) {
            userPO = new UserPO();
            userInfo.setId(null);
        }
        ignorePropList.add("lastUpdated");
        String[] ignoreProps = new String[ignorePropList.size()];
        ignorePropList.toArray(ignoreProps);
        BeanUtils.copyProperties(userInfo, userPO, ignoreProps);
        userPO.setLastUpdated(DateUtils.now());
        if (!isPersist) {
            entityManager.merge(userPO);
        } else {
            if(userInfo.getRole().equals("ROLE_VENDOR")){ // 会员开通的时候默认是不可启用状态，审核状态是待审核，积分默认10分
                userPO.setStatus(UserStatus.DISABLE.getKey());
                userPO.setAuditsStatus(AuditStatus.WAIT.getKey());
                userPO.setScore(10);
            }
            entityManager.persist(userPO);

            String role = userInfo.getRole();
            RolePO rolePO = roleRepository.getRolePOByName(role);
            if (rolePO == null) {
                throw new RuntimeException("角色不存在：" + role);
            }
            UserRolePO userRolePO = new UserRolePO();
            userRolePO.setRid(rolePO.getId());
            userRolePO.setUid(userPO.getId());
            entityManager.persist(userRolePO);

            List<UserChildInfo> child = userInfo.getUserChildInfoList();
            if(child!=null && child.size() > 0){
                for(UserChildInfo userChildInfo:child){
                    UserChildPO userChildPO = new UserChildPO();
                    BeanUtils.copyProperties(userChildInfo, userChildPO);
                    userChildPO.setUserId(userPO.getId());
                    entityManager.persist(userChildPO);
                }
            }
        }

        entityManager.flush();
    }

    @Override
    public TokenPO getTokenPOById(String id) {
        Optional<TokenPO> byId = tokenRepository.findById(id);
        TokenPO tokenPO = null;
        if (byId.isPresent()) {
            tokenPO = byId.get();
        }
        return tokenPO;
    }

    @Override
    @Transactional
    public void deleteTokenPOById(String id) {
        tokenRepository.deleteById(id);
    }

    @Override
    public TokenPO getTokenPOByAccount(String account) {
        return tokenRepository.getTokenPOByAccount(account);
    }

    @Override
    public UserInfo getUserInfoByUserBO(UserBO userBO) {
        UserInfo userInfo = getUserInfoById(userBO.getId());
        userInfo.setRole(userBO.getRole().get(0).getName());

        return userInfo;
    }

    @Override
    public long findAll(final UserSearch body, List<UserInfo> list) {

        int page = 1;
        int size = 10;
        Integer pageNum = body.getPageNum();
        if (pageNum != null) {
            page = pageNum;
        }
        page -= 1;
        Integer pageSize = body.getPageSize();
        if (pageSize != null) {
            size = pageSize;
        }
        // 根据动态条件从数据库分页查询用户信息
        Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "lastUpdated");

        Specification<UserPO> specification = (Specification<UserPO>) (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            String name = body.getName();
            if (StringUtils.hasLength(name)) {
                predicates.add(builder.like(root.get("name").as(String.class), "%" + name + "%"));
            }
            String email = body.getEmail();
            if (StringUtils.hasLength(email)) {
                predicates.add(builder.like(root.get("email").as(String.class), "%" + email + "%"));
            }
            String phone = body.getPhone();
            if (StringUtils.hasLength(phone)) {
                predicates.add(builder.like(root.get("phone").as(String.class), "%" + phone + "%"));
            }
            String status = body.getStatus();
            if (StringUtils.hasLength(status)) {
                predicates.add(builder.equal(root.get("status").as(String.class), status));
            }
            String auditsStatus = body.getAuditsStatus();
            if (StringUtils.hasLength(auditsStatus)) {
                predicates.add(builder.equal(root.get("auditsStatus").as(String.class), auditsStatus));
            }

            String enterpriseName = body.getEnterpriseName();
            if (StringUtils.hasLength(enterpriseName)) {
                predicates.add(builder.like(root.get("company").as(String.class), "%" + enterpriseName + "%"));
            }

            String userRole = body.getUserRole();
            if (StringUtils.hasLength(userRole)) {
                predicates.add(builder.like(root.get("role").as(String.class), userRole));
            }
            return builder.and(predicates.toArray(new Predicate[0]));
        };
        Page<UserPO> pageAll = userRepository.findAll(specification, pageable);
        List<UserPO> all = pageAll.getContent();
        for (UserPO userPO : all) {
            list.add(getUserInfo(userPO));
        }
        return pageAll.getTotalElements();
    }

    private UserInfo getUserInfo(UserPO userPO) {
        UserInfo info = new UserInfo();
        BeanUtils.copyProperties(userPO, info);

        if(info.getRole().equals("ROLE_VENDOR") && info.getMemberRole().equals("teacher")){
            List<UserChildPO> userChildPOList = userChildRepository.getUserChildPOSByUserId(info.getId());
            List<UserChildInfo> userChildInfos = new LinkedList<>();
            for(UserChildPO userChildPO:userChildPOList){
                UserChildInfo userChildInfo = new UserChildInfo();
                BeanUtils.copyProperties(userChildPO, userChildInfo);
                userChildInfos.add(userChildInfo);
            }
            info.setUserChildInfoList(userChildInfos);
        }
        return info;
    }

    @Override
    public List<RoleInfo> findAllRole() {
        List<RoleInfo> infoList = new ArrayList<>();
        List<RolePO> all = roleRepository.findAll();
        for (RolePO rolePO : all) {
            RoleInfo roleInfo = new RoleInfo();
            BeanUtils.copyProperties(rolePO, roleInfo);
            infoList.add(roleInfo);
        }
        return infoList;
    }

    @Override
    public void validUserInfo(UserInfo userInfo) {
        Assert.notNull(userInfo.getAccount(), "[账号]不能为空");
        Assert.notNull(userInfo.getName(), "[中文名]不能为空");
        Assert.notNull(userInfo.getPhone(), "[手机号]不能为空");
        Assert.notNull(userInfo.getEmail(), "[电子邮箱]不能为空");
        Assert.notNull(userInfo.getPasswd(), "[密码]不能为空");
        String role = userInfo.getRole();
        Assert.notNull(role, "[角色]不能为空");
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserPO user = userRepository.getUserPOByAccount(username);
        UserBO userBO = new UserBO();
        if (user == null) {
            return null;
        }
        BeanUtils.copyProperties(user, userBO);
        List<RoleBO> roleBOList = new ArrayList<>();
        List<UserRolePO> userRolePOList = userRoleRespository.getUserRolePOSByUid(user.getId());
        for (UserRolePO userRolePO : userRolePOList) {
            Optional<RolePO> roleList = roleRepository.findById(userRolePO.getRid());
            if (!roleList.isPresent()) {
                continue;
            }
            RolePO rolePO = roleList.get();
            RoleBO roleBO = new RoleBO();
            BeanUtils.copyProperties(rolePO, roleBO);
            roleBOList.add(roleBO);
        }
        userBO.setRole(roleBOList);
        return userBO;
    }

    @Override
    public List<Status> findAllStatus(String role) {
        List<UserPO> all = userRepository.findAllByRole(role);
        List<Status> statusList = new LinkedList<>();

        statusList.add(new Status().key("ALL").name("全部").value(all.size()));

        if(role.equals("ROLE_VENDOR")){
            Map<String, Long> collect = all.stream().collect(Collectors.groupingBy(UserPO::getAuditsStatus, Collectors.counting()));
            AuditStatus[] statusAll = AuditStatus.values();
            for (AuditStatus auditStatus:statusAll){
                if(!auditStatus.getKey().equals("ALL")){
                    Status status = adapterStatus(collect, auditStatus.getKey(), auditStatus.getName());
                    statusList.add(status);
                }
            }
        } else if(role.equals("ROLE_HRBP")){
            Map<String, Long> collect = all.stream().collect(Collectors.groupingBy(UserPO::getStatus, Collectors.counting()));
            UserStatus[] statusAll = UserStatus.values();
            for (UserStatus userStatus:statusAll){
                if(!userStatus.getKey().equals("ALL")){
                    Status status = adapterStatus(collect, userStatus.getKey(), userStatus.getName());
                    statusList.add(status);
                }
            }
        }

        return statusList;
    }

    private Status adapterStatus(Map<String, Long> collect, String key, String name){
        int value = 0;
        if(collect.containsKey(key)){
            value = collect.get(key).intValue();
        }
        Status status = new Status().key(key).name(name).value(value);
        return status;
    }

    @Override
    @Transactional
    public void changePassword(String userId, String tokenId, String newPasswrod, String encodePasswd) {
        Optional<UserPO> byId = userRepository.findById(userId);
        if (!byId.isPresent()) {
            throw new RuntimeException("用户不存在！");
        }
        UserPO userPO = byId.get();
        userPO.setPasswd(encodePasswd);
        userPO.setOriginPwd(newPasswrod);
        userPO.setLastUpdated(DateUtils.now());
        entityManager.merge(userPO);
        tokenRepository.deleteById(tokenId);
    }

    @Override
    @Transactional
    public void changeStatus(List<Body> bodies) {
        for (Body body : bodies) {
            String id = body.getId();
            if (!StringUtils.hasLength(id)) {
                throw new RuntimeException("用户ID不能为空");
            }
            String status = body.getStatus();
            if (!StringUtils.hasLength(status)) {
                throw new RuntimeException("用户状态不能为空");
            }
            // 校验用户状态有效
            UserStatus.getNameByKey(status);
            Optional<UserPO> byId = userRepository.findById(id);
            if (!byId.isPresent()) {
                throw new RuntimeException("用户不存在！");
            }
            UserPO userPO = byId.get();
            if ("admin".equals(userPO.getAccount())) {
                throw new RuntimeException("不允许操作此用户！");
            }
            userPO.setStatus(status);
            userPO.setLastUpdated(DateUtils.now());
            entityManager.merge(userPO);
        }
        entityManager.flush();
    }

    @Override
    @Transactional
    public void changeAuditStatus(List<Body> bodies,String loginUrl){
        for (Body body : bodies) {
            String id = body.getId();
            if (!StringUtils.hasLength(id)) {
                throw new RuntimeException("用户ID不能为空");
            }
            String status = body.getStatus();
            if (!StringUtils.hasLength(status)) {
                throw new RuntimeException("用户审核状态不能为空");
            }
            // 校验用户状态有效
            AuditStatus.getNameByKey(status);
            Optional<UserPO> byId = userRepository.findById(id);
            if (!byId.isPresent()) {
                throw new RuntimeException("用户不存在！");
            }
            UserPO userPO = byId.get();
            userPO.setAuditsStatus(status);
            if(status.equals("PASS")){
                userPO.setStatus(UserStatus.ENABLE.getKey());
            } else {
                userPO.setReason(body.getReason());
                userPO.setStatus(UserStatus.DISABLE.getKey());
            }
            userPO.setLastUpdated(DateUtils.now());
            entityManager.merge(userPO);

            if(status.equals("PASS")) {
                mailService.send(userPO.getEmail(), userPO.getName(), userPO.getAccount(), userPO.getOriginPwd(), loginUrl, "会员");
            } else {
                mailService.sendAuditFailMsg(userPO.getEmail(), userPO.getName(), userPO.getReason());
            }
        }
        entityManager.flush();
    }
}
